#include "Mesh.h"

Mesh::Mesh() : m_pMesh(NULL), m_numMaterials(0), m_pMeshMaterials(NULL), m_ppMeshTextures(NULL) {}

void Mesh::Release() {
	// delete the materials
	SAFE_DELETE_ARRAY(m_pMeshMaterials);

	// delete the textures
	if(m_ppMeshTextures) {
		for(DWORD i=0; i<m_numMaterials; i++)
			SAFE_RELEASE(m_ppMeshTextures[i]);
	}
	SAFE_DELETE_ARRAY(m_ppMeshTextures);
	SAFE_RELEASE(m_pMesh);
}

bool Mesh::Load(LPDIRECT3DDEVICE9 pDevice, char* file) {
	Release();
	LPD3DXBUFFER pMaterialBuffer;
	HRESULT hr = D3DXLoadMeshFromX(file, /*D3DXMESH_SYSTEMMEM*/D3DXMESH_MANAGED, pDevice, NULL, &pMaterialBuffer, NULL, &m_numMaterials, &m_pMesh);
	if(FAILED(hr)) {
		SHOWERROR("D3DXLoadMeshFromX - Failed", __FILE__, __LINE__);
		return false;
	}

	// store material and texture information
	D3DXMATERIAL* pMaterials = (D3DXMATERIAL*)pMaterialBuffer->GetBufferPointer();

	m_pMeshMaterials = new D3DMATERIAL9[m_numMaterials];
	m_ppMeshTextures = new LPDIRECT3DTEXTURE9[m_numMaterials];

	// copy the materials and textures from the buffer to the member arrays
	for(DWORD i=0; i<m_numMaterials; i++) {
		m_pMeshMaterials[i] = pMaterials[i].MatD3D;
		m_pMeshMaterials[i].Ambient = m_pMeshMaterials[i].Diffuse;
		// create the texture if it exists
		m_ppMeshTextures[i] = NULL;
		if(pMaterials[i].pTextureFilename) {
			string tempLocation = g_TextureLocation;
			tempLocation.append(pMaterials[i].pTextureFilename);
			if(FAILED(D3DXCreateTextureFromFile(pDevice, tempLocation.c_str(), &m_ppMeshTextures[i]))) {
				SHOWERROR("Failed to load mesh texture", __FILE__, __LINE__);
				return false;
			}
		}
	}

	// don't need this anymore
	pMaterialBuffer->Release();

	return true;
}

bool Mesh::Load(LPDIRECT3DDEVICE9 pDevice, string file) {
	Release();
	LPD3DXBUFFER pMaterialBuffer;
	HRESULT hr = D3DXLoadMeshFromX(file.c_str(), D3DXMESH_MANAGED, pDevice, NULL, &pMaterialBuffer, NULL, &m_numMaterials, &m_pMesh);
	if(FAILED(hr)) {
		SHOWERROR("D3DXLoadMeshFromX - Failed", __FILE__, __LINE__);
		return false;
	}

	// store material and texture information
	D3DXMATERIAL* pMaterials = (D3DXMATERIAL*)pMaterialBuffer->GetBufferPointer();

	m_pMeshMaterials = new D3DMATERIAL9[m_numMaterials];
	m_ppMeshTextures = new LPDIRECT3DTEXTURE9[m_numMaterials];

	// copy the materials and textures from the buffer to the member arrays
	for(DWORD i=0; i<m_numMaterials; i++) {
		m_pMeshMaterials[i] = pMaterials[i].MatD3D;
		m_pMeshMaterials[i].Ambient = m_pMeshMaterials[i].Diffuse;
		// create the texture if it exists
		m_ppMeshTextures[i] = NULL;
		if(pMaterials[i].pTextureFilename) {
			string tempLocation = g_TextureLocation;
			tempLocation.append(pMaterials[i].pTextureFilename);
			if(FAILED(D3DXCreateTextureFromFile(pDevice, tempLocation.c_str(), &m_ppMeshTextures[i]))) {
				SHOWERROR("Failed to load mesh texture", __FILE__, __LINE__);
				return false;
			}
		}
	}

	// don't need this anymore
	pMaterialBuffer->Release();

	return true;
}


// MeshInstance

MeshInstance::MeshInstance() : m_pMesh(NULL), m_boundingRadius(0.0f) {}

void MeshInstance::Release() {
	m_pMesh = NULL;
}

void MeshInstance::setMesh(Mesh* pMesh) {
	Release();
	m_pMesh = pMesh; 

	// compute bounding sphere
	if(m_pMesh) {
		D3DXVECTOR3 center;
		LPD3DXMESH pD3DXMesh = m_pMesh->getMesh();
		DWORD numVertices = pD3DXMesh->GetNumVertices();
		DWORD fvfSize = D3DXGetFVFVertexSize(pD3DXMesh->GetFVF());
		char* pData = NULL;
		if(FAILED(pD3DXMesh->LockVertexBuffer(0, (void**)&pData))) {
			SHOWERROR("Failed to lock mesh vertex buffer.", __FILE__, __LINE__);
			return;
		}
		D3DXComputeBoundingSphere((D3DXVECTOR3*)pData, numVertices, fvfSize, &center, &m_boundingRadius);
		if(FAILED(pD3DXMesh->UnlockVertexBuffer())) {
			SHOWERROR("Failed to unlock mesh vertex buffer.", __FILE__, __LINE__);
			return;
		}
	}
}

void MeshInstance::Render(LPDIRECT3DDEVICE9 pDevice) {
	if(pDevice && m_pMesh) {
		pDevice->SetTransform(D3DTS_WORLD, getTransform());
		DWORD numMaterials = m_pMesh->getNumMaterials();
		for(DWORD i=0; i<numMaterials; i++) {
			pDevice->SetMaterial(m_pMesh->getMeshMaterial(i));
			pDevice->SetTexture(0, m_pMesh->getMeshTexture(i));
			m_pMesh->getMesh()->DrawSubset(i);
		}
	}
}
